package cmd

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/spf13/cobra"

	"gitee.com/okatu-loli/oc_contributor_fanyuxiao/pkg/builder"
	"gitee.com/okatu-loli/oc_contributor_fanyuxiao/pkg/image"
	"gitee.com/okatu-loli/oc_contributor_fanyuxiao/pkg/security"
	"gitee.com/okatu-loli/oc_contributor_fanyuxiao/pkg/workflow"
)

var (
	buildMode string
	imageRef  string
	push      bool
	output    string
	analyze   bool
	scan      bool
)

// buildCmd represents the build command
var buildCmd = &cobra.Command{
	Use:   "build",
	Short: "Build container image for the current Go project",
	Long: `Builds a container image for the current project using an
OpenCloudOS 9 base image and analyzed dependencies. By default the generated
image is scanned for vulnerabilities and reports are written to the current
directory.`,
	Example: `  # Build and scan the image, then push it to a registry
  oc_contributor_fanyuxiao build --image myrepo/app:latest --push`,
	Run: func(cmd *cobra.Command, args []string) {
		base, err := image.SelectBaseImage(buildMode)
		if err != nil {
			fmt.Fprintf(os.Stderr, "select base image: %v\n", err)
			return
		}
		fmt.Printf("Selected base image: %s\n", base)

		var files []string
		if analyze {
			var err error
			files, err = workflow.Run(context.Background(), workflow.Options{ProjectDir: "."})
			if err != nil {
				fmt.Fprintf(os.Stderr, "analysis: %v\n", err)
			}
		}

		tmpDir, err := os.MkdirTemp("", "oc-build-")
		if err != nil {
			fmt.Fprintf(os.Stderr, "create temp dir: %v\n", err)
			return
		}
		defer os.RemoveAll(tmpDir)
		binaryPath := filepath.Join(tmpDir, "app")
		buildCmd := exec.Command("go", "build", "-o", binaryPath, ".")
		if out, err := buildCmd.CombinedOutput(); err != nil {
			fmt.Fprintf(os.Stderr, "go build: %v\n%s", err, string(out))
			return
		}
		files = append([]string{binaryPath}, files...)

		tarPath := output
		var tmpTar string
		if scan && !push && tarPath == "" {
			tmp, err := os.CreateTemp("", "oc-build-*.tar")
			if err != nil {
				fmt.Fprintf(os.Stderr, "create temp tarball: %v\n", err)
				return
			}
			tmp.Close()
			tarPath = tmp.Name()
			tmpTar = tmp.Name()
			defer os.Remove(tmpTar)
		}

		opts := builder.Options{
			Files:        files,
			ImageRef:     imageRef,
			Push:         push,
			LocalTarPath: tarPath,
			BaseImage:    base,
		}
		if err := builder.Build(context.Background(), opts); err != nil {
			fmt.Fprintf(os.Stderr, "build image: %v\n", err)
			return
		}

		if scan {
			target := ""
			if push && imageRef != "" {
				target = imageRef
			} else if tarPath != "" {
				target = tarPath
			}
			if target != "" {
				if err := security.ScanImage(target, false); err != nil {
					if errors.Is(err, security.ErrTrivyNotFound) {
						fmt.Println("Trivy not found, skipping scan.")
					} else {
						fmt.Fprintf(os.Stderr, "image scan failed: %v\n", err)
					}
				} else {
					fmt.Println("Scan reports written to scan-result.json and scan-report.html")
				}
			}
		}

		if push && imageRef != "" {
			fmt.Println("Pushed image:", imageRef)
		} else if imageRef != "" {
			fmt.Println("Built image:", imageRef)
		}
		if output != "" {
			fmt.Println("Wrote tarball:", output)
		}
	},
}

func init() {
	rootCmd.AddCommand(buildCmd)

	buildCmd.Flags().StringVarP(&buildMode, "mode", "m", "production", "build mode (production|debug)")
	buildCmd.Flags().StringVar(&imageRef, "image", "", "image reference")
	buildCmd.Flags().BoolVar(&push, "push", false, "push image to registry")
	buildCmd.Flags().StringVarP(&output, "output", "o", "", "write image to tar file")
	buildCmd.Flags().BoolVar(&analyze, "analyze", false, "run full dependency analysis before build")
	buildCmd.Flags().BoolVar(&scan, "scan", true, "scan built image for vulnerabilities")
}
